#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

static void usage(const char *cmd)
{
    printf("Usage: %s </[rwx]> </file> <filepath>\n", cmd);
    printf("       %s </[rwx]> </anon> <size>\n", cmd);
}

static int inner_parse_prot(const char *str, int &oflag, int &prot)
{
    int __oflag = O_RDONLY;
    int __prot = PROT_NONE;

    if('/' != *str)
    {
        printf("Invalid parameter:%s\n", str);
        return EINVAL;
    }
    ++str;
    while ('\0' != *str)
    {
        switch (*str)
        {
        case 'r':
            __oflag = O_RDONLY;
            __prot |= PROT_READ;
            break;
        case 'w':
            __oflag = O_WRONLY;
            __prot |= PROT_WRITE;
            break;
        case 'x':
            __oflag = O_RDWR;
            __prot |= PROT_EXEC;
            break;
        default:
            printf("Invalid parameter:%s\n", str);
            return EINVAL;
        }
        ++str;
    }
    oflag = __oflag;
    prot = __prot;
    return 0;
}

static int inner_mmap_file(const char *filename, int &oflag, int prot)
{
    int ret = 0;
    int fd;
    struct stat st;
    unsigned char *addr = (unsigned char *)MAP_FAILED;

    fd = open(filename, oflag);
    if (-1 == fd)
    {
        ret = errno;
        printf("open <%s> failed!ret=%d\n", filename, ret);
        goto _OUT;
    }
    if (0 != fstat(fd, &st))
    {
        ret = errno;
        printf("fstat <%s> failed!ret=%d\n", filename, ret);
        goto _OUT;
    }
    addr = (unsigned char *)mmap(NULL, st.st_size, prot, MAP_PRIVATE, fd, 0);
    if (MAP_FAILED == addr)
    {
        ret = errno;
        printf("mmap <%s> failed!ret=%d\n", filename, ret);
        goto _OUT;
    }
    ret = mprotect(addr, st.st_size, PROT_READ|PROT_WRITE|PROT_EXEC);
    if(-1 == ret)
    {
        ret = errno;
        printf("mprotect <%s> failed!ret=%d\n", filename, ret);
        goto _OUT;
    }
    printf("press any key to exit...");
    getchar();
_OUT:
    if (MAP_FAILED != addr)
    {
        munmap(addr, st.st_size);
    }
    if (-1 != fd)
    {
        close(fd);
    }
    return ret;
}

static int inner_mmap_anonymous(int prot, int size)
{
    int ret = 0;
    unsigned char *addr = (unsigned char *)MAP_FAILED;

    addr = (unsigned char *)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (MAP_FAILED == addr)
    {
        ret = errno;
        printf("mmap anonymous failed!ret=%d\n", ret);
        goto _OUT;
    }
    ret = mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC);
    if(-1 == ret)
    {
        ret = errno;
        printf("mmap anonymous failed!ret=%d\n", ret);
        goto _OUT;
    }
    printf("press any key to exit...");
    getchar();
_OUT:
    if (MAP_FAILED != addr)
    {
        munmap(addr, size);
    }
    return ret;
}

int main(int argc, char **argv)
{
    int ret;
    int oflag = 0;
    int prot = PROT_NONE;
    int size;

    if(argc < 4)
    {
        usage(argv[0]);
        return EINVAL;
    }
    else
    {
        ret = inner_parse_prot(argv[1], oflag, prot);
        if(0 != ret)
        {
            return ret;
        }
        if(0 == strcmp("/file", argv[2]))
        {
            ret = inner_mmap_file(argv[3], oflag, prot);
        }
        else if (0 == strcmp("/anon", argv[2]))
        {
            size = atoi(argv[3]);
            if(size <= 0)
            {
                printf("Invalid parameter:%s!\n", argv[3]);
                return EINVAL;
            }
            ret = inner_mmap_anonymous(prot, size);
        }
        
    }
    return ret;
}
